/***************************************************************************                            *
 *Author: Achilles Zhang												   *
 *Date: 2012.01.28														   *
 *File Name: ShadowCaster.cpp											   *
 *Description: The file is used to declare the function of shadow caster	       *
 ***************************************************************************/
#include "ShadowCaster.h"
#include "math.h"

ShadowCaster* ShadowCaster::_singleton = 0;
ShadowCasterStruct* ShadowCaster::tss[4] = {0, 0, 0, 0};
bool ShadowCaster::isShadering = true;
bool ShadowCaster::isInit = false;
float ShadowCaster::minLightDistance = 5.0f;
float ShadowCaster::maxLightDistance = 50.0f;
float ShadowCaster::lightRadius = 10.0f;
int ShadowCaster::shadowSize = 128;
CKBOOL ShadowCaster::isSoft = false;
VxVector ShadowCaster::barycenter(0.0f,0.0f,0.0f);
VxVector ShadowCaster::minV(0.0f,0.0f,0.0f);
VxVector ShadowCaster::maxV(0.0f, 0.0f, 0.0f);
VxVector ShadowCaster::lightPos(0.0f,10.0f,0.0f);
float ShadowCaster::angle = 90.00f;
CK_ID ShadowCaster::lightID = -1;

char* materialname[4] = {"SC Material_1", "SC Material_2", "SC Material_3", "SC Material_4"};
char* blackmaterialname[4] = {"ShadowCasterBlackMaterial_1","ShadowCasterBlackMaterial_2","ShadowCasterBlackMaterial_3","ShadowCasterBlackMaterial_4",};
char* cameraname[4] = {"ShadowCaster Camera_1", "ShadowCaster Camera_2", "ShadowCaster Camera_3", "ShadowCaster Camera_4"};
char* texturename[4] = {"ShadowCaster Texture_1","ShadowCaster Texture_2","ShadowCaster Texture_3","ShadowCaster Texture_4"};

ShadowCaster::ShadowCaster(){
	dialog = new ShadowCasterDialog;
	//for(int i = 0; i<4; i++){
		initialization();
		//if (tss[i] == 0){
		//	tss[i] = new ShadowCasterStruct;
		//	loadShadowCaster(tss[i], i);
		//}
	//}
}

void ShadowCaster::initialization(){
	if(tss[0] == 0){
		tss[0] = new ShadowCasterStruct;
	}
	loadShadowCaster(tss[0], 0);
}

ShadowCaster::~ShadowCaster(){

}

ShadowCaster* ShadowCaster::Instance(){
	if(_singleton == 0)
		_singleton = new ShadowCaster;
	return _singleton;
}

void ShadowCaster::destruct(){
	delete _singleton;
	_singleton = NULL;
}

void ShadowCaster::show(){
	dialog->show();
}

void ShadowCaster::HierarchicalRendering(CK3dEntity* ent,CKRenderContext* dev,ShadowCasterStruct* tss)
{
	if (ent->IsHierarchicallyHide()) return;

	if (ent->IsVisible()) {	

		//--- Test if one material is transparent
		CKMesh* mesh = ent->GetCurrentMesh();
		CKBOOL changeForAlpha = FALSE;
		if( mesh && mesh->IsVisible() ){

			const int matCount = mesh->GetMaterialCount();
			for( int a=0 ; a<matCount ; ++a )
			{
				CKMaterial* mat = mesh->GetMaterial( a );
				if( !mat ) continue;
// #ifndef NO_SHADER				
// 				if( !mat->GetMaterialShader(FALSE) )
// #endif				
				{
					if( mat->IsAlphaTransparent() || mat->AlphaTestEnabled() ){
						changeForAlpha = TRUE;
						break;
					} else {
						CKTexture* tex = mat->GetTexture();
						if( tex ){
							if( tex->IsTransparent() ){
								changeForAlpha = TRUE;
								break;
							}
						}
					}
				}
			}

			//--- So if object contains a transparent material
			// render it without the overridding material
			// because we must take the alpha into account 
		//	CKRenderContext* rc = ent->GetCKContext()->GetRenderManager()->GetRenderContext(0);
			CKRenderContext* rc =dev;
			CKMaterial* overMat = tss->m_ReplacingMaterial? tss->m_ReplacingMaterial:tss->m_BlackMaterial;

			if( changeForAlpha ){
				rc->SetOverriddenRendering(CK_OVERRIDE_NONE);
				ent->Render(dev,0);

				// And restore current material settings
				overMat->SetAsCurrent(rc);
				rc->SetTextureStageState(CKRST_TSS_STAGEBLEND,0,1);
				rc->SetOverriddenRendering(CK_OVERRIDE_USECURRENT,overMat);
				rc->SetState(VXRENDERSTATE_LIGHTING,TRUE);

			} else {
				//--- Render the Object
				ent->Render(dev,0);
			}

		}
	}

	// rendering the children
	for (int i=0; i<ent->GetChildrenCount(); ++i) {
		HierarchicalRendering(ent->GetChild(i),dev,tss);
	} 
}


void ShadowCaster::DrawFillRectangleGradient(CKRenderContext *dev,VxRect& rect,const VxColor& start, const VxColor& end)
{
	CKDWORD oldfog=0;
	oldfog = dev->GetState(VXRENDERSTATE_FOGENABLE);

	dev->SetTexture(NULL);
	dev->SetState(VXRENDERSTATE_ALPHATESTENABLE,FALSE);
	dev->SetState(VXRENDERSTATE_CULLMODE,		VXCULL_NONE);
	dev->SetState(VXRENDERSTATE_FOGENABLE,		FALSE);
	dev->SetState(VXRENDERSTATE_ZFUNC,			VXCMP_ALWAYS);
	dev->SetState(VXRENDERSTATE_ZWRITEENABLE ,	FALSE);
	dev->SetState(VXRENDERSTATE_ALPHABLENDENABLE,	TRUE);
	dev->SetState(VXRENDERSTATE_SRCBLEND,		VXBLEND_ONE);
	dev->SetState(VXRENDERSTATE_DESTBLEND,		VXBLEND_ONE);

	VxDrawPrimitiveData* data;
	data = dev->GetDrawPrimitiveStructure(CKRST_DP_CL_VC,4);

	XPtrStrided<VxVector4> positions(data->Positions);
	/////////////////
	// Colors

	DWORD col = RGBAFTOCOLOR(&start);
	VxFillStructure(2,data->Colors,sizeof(DWORD),&col);
	col = RGBAFTOCOLOR(&end);
	VxFillStructure(2,(BYTE*)data->Colors.Ptr+2*data->Colors.Stride,data->Colors.Stride,sizeof(DWORD),&col);

	/////////////////
	// Positions

	// Vertex 0
	positions->x = rect.left;
	positions->y = rect.top;
	positions->z = 0.0f;
	positions->w = 1.0f;
	++positions;
	// Vertex 1
	positions->x = rect.right;
	positions->y = rect.top;
	positions->z = 0.0f;
	positions->w = 1.0f;
	++positions;
	// Vertex 2
	positions->x = rect.right;
	positions->y = rect.bottom;
	positions->z = 0.0f;
	positions->w = 1.0f;
	++positions;
	// Vertex 3
	positions->x = rect.left;
	positions->y = rect.bottom;
	positions->z = 0.0f;
	positions->w = 1.0f;
	++positions;

	// Indices
	CKWORD* indices = dev->GetDrawPrimitiveIndices(4);
	indices[0] = 0;
	indices[1] = 1;
	indices[2] = 2;
	indices[3] = 3;

	// the drawing itself
	dev->DrawPrimitive( VX_TRIANGLEFAN, indices, 4, data );

	if (oldfog != VXFOG_NONE) dev->SetState(VXRENDERSTATE_FOGENABLE,		TRUE);
}

CKBOOL ShadowCaster::RenderObjectsToTexture(CKContext* ctx,/*CK3dEntity* ent,*/CKRenderContext* rc, ShadowCasterStruct* tss, CK3dEntity* light, float lmin,float lmax,float& minz,CKBOOL softshadow, float *retrievedMaxx, float *retrievedMaxy)
{
	CKTexture* tex = tss->m_ShadowTexture;
	CKCamera* cam= tss->m_Camera;

	//
	// storing old render context values	

	CKMaterial *BackgroundMat	 = rc->GetBackgroundMaterial();
	CKCamera* oldcam = rc->GetAttachedCamera(); // we store the old camera
	CKTexture *oldtex= BackgroundMat->GetTexture(); //we store the old texture
	VxColor oldcol = BackgroundMat->GetDiffuse();

	///////////////////////////////
	// Object Light interactions
	//VxVector minV;
	//VxVector maxV;
	//minV = CCustomPlayer::Instance().getShadowCasterObject(0)->GetHierarchicalBox().Min;
	//maxV = CCustomPlayer::Instance().getShadowCasterObject(0)->GetHierarchicalBox().Max;

	//for(int i = 0; i<CCustomPlayer::Instance().getShadowCasterNum(); i++){
	////	if(rc->IsObjectAttached(CCustomPlayer::Instance().getShadowCasterObject(i))){
	//		VxVector tempMin = CCustomPlayer::Instance().getShadowCasterObject(i)->GetHierarchicalBox().Min;
	//		VxVector tempMax = CCustomPlayer::Instance().getShadowCasterObject(i)->GetHierarchicalBox().Max;
	//		if(tempMin.x<minV.x)
	//			minV.x = tempMin.x;
	//		if(tempMin.y<minV.y)
	//			minV.y = tempMin.y;
	//		if(tempMin.z<minV.z)
	//			minV.z = tempMin.z;
	//		if(tempMax.x>maxV.x)
	//			maxV.x = tempMax.x;
	//		if(tempMax.y>maxV.y)
	//			maxV.y = tempMax.y;
	//		if(tempMax.z>maxV.z)
	//			maxV.z = tempMax.z;
	////	}
	//}
	//const VxBbox& entbox = ent->GetHierarchicalBox();
	caculateEntBox();
	VxBbox entbox(minV, maxV);
	VxVector centerbox = entbox.GetCenter();

	VxVector lightpos;
	light->GetPosition(&lightpos);
	//light->SetPosition(centerbox.x, lightPos.y, centerbox.z);
	//light->GetPosition(&lightPos);

	cam->SetPosition(&lightpos);
	cam->LookAt(&centerbox);

	VxVector posTest1;
	cam->GetPosition(&posTest1);

	lightpos -= centerbox;
	float distance = Magnitude(lightpos);
	lightpos += centerbox;

	VxBbox localbox;
	localbox.TransformFrom(const_cast<VxBbox&>(entbox),cam->GetInverseWorldMatrix());

	// the light is considered too far away
	CKBOOL render = TRUE;
	if (distance >= lmax) {
		render = FALSE;
		distance = lmax;
	}

	float delta = (distance-lmin)/(lmax-lmin);
	if (delta < 0.0f) 
		delta = 0.0f;
	else 
		if (delta > 1.0f) 
			delta = 1.0f;

	// We set the background color
	VxColor shadowColor(1.0f,1.0f,1.0f,1.0f);
	BackgroundMat->SetDiffuse(shadowColor);
	BackgroundMat->SetTexture(NULL);

	// the color of the shadow
	shadowColor *= delta;

	const VxMatrix& invcam = cam->GetInverseWorldMatrix();

	VxVector pts[8];
	entbox.TransformTo(pts,invcam);

	VxVector tcenter;
	Vx3DMultiplyMatrixVector(&tcenter,invcam,&centerbox);

	// we find the maxima of the transformed points
	float minx=pts[0].x,maxx=pts[0].x,miny=pts[0].y,maxy=pts[0].y,maxz=pts[0].z;
	minz=pts[0].z;
	for(int i=1;i<8;i++) {
		if(pts[i].x < minx) minx = pts[i].x;
		else if(pts[i].x > maxx) maxx = pts[i].x;
		if(pts[i].y < miny) miny = pts[i].y;
		else if(pts[i].y > maxy) maxy = pts[i].y;
		if(pts[i].z < minz) minz = pts[i].z;
		else if(pts[i].z > maxz) maxz = pts[i].z;
	}
	maxx -= minx;
	maxy -= miny;

	*retrievedMaxx = maxx;
	*retrievedMaxy = maxy;


	float rapport;
	float tanfov;
	if(maxx > maxy) {
		rapport = maxx*0.5f;
	} else {
		rapport = maxy*0.5f;
	}
	float fov = cam->GetFov();
	tanfov = rapport/tanf(fov*0.5f);

	cam->GetPosition(&posTest1);

	cam->Translate(0,0,-(tanfov-minz),cam);
	
	tss->m_Camera->GetPosition(&posTest1);
	
	// Setup the front & back plane to match the box 
	// of the object we are rendering 
	maxz -= -(tanfov-minz);
	minz -= -(tanfov-minz);
	minz *= 0.1f;
	maxz *= 2.0f;
	if (minz < 0.01f) minz = 0.01f;
	if (maxz > 10000.0f) maxz = 10000.0f;
	cam->SetFrontPlane(minz);
	cam->SetBackPlane(maxz);

	cam->GetPosition(&posTest1);
	tss->m_Camera->GetPosition(&posTest1);

	rc->AttachViewpointToCamera(cam);
	BOOL srt = rc->SetRenderTarget(tex);
	if (srt) {
		// We prepare the cameras
		DWORD RenderOptions = rc->GetCurrentRenderOptions();
		RenderOptions &= ~(CK_RENDER_USECAMERARATIO|CK_RENDER_DOBACKTOFRONT);
		rc->PrepareCameras((CK_RENDER_FLAGS)RenderOptions);

		// we Clear the back buffer
		rc->Clear((CK_RENDER_FLAGS)(CK_RENDER_CLEARBACK|CK_RENDER_CLEARZ|CK_RENDER_CLEARVIEWPORT));

		if (render) {
			// Here Comes the rendering
			rc->SetCurrentMaterial(NULL);
			rc->SetState(VXRENDERSTATE_ALPHATESTENABLE,		0);
			rc->SetState(VXRENDERSTATE_WRAP0 ,				0);
			rc->SetState(VXRENDERSTATE_CULLMODE,			VXCULL_CW);

			rc->SetState(VXRENDERSTATE_SRCBLEND,			VXBLEND_ONE);
			rc->SetState(VXRENDERSTATE_DESTBLEND,			VXBLEND_ZERO);
			rc->SetState(VXRENDERSTATE_ALPHABLENDENABLE,	FALSE);
			rc->SetState(VXRENDERSTATE_ZENABLE ,			FALSE);
			rc->SetState(VXRENDERSTATE_ZWRITEENABLE ,		FALSE);

			///
			// Saving Matrix
			VxMatrix oldViewMat		= rc->GetViewTransformationMatrix();
			VxMatrix oldProjMat		= rc->GetProjectionTransformationMatrix();
			VxMatrix oldWordMatrix	= rc->GetWorldTransformationMatrix();

			rc->SetViewTransformationMatrix(cam ->GetInverseWorldMatrix());
			VxMatrix projmat;cam->ComputeProjectionMatrix(projmat);
			rc->SetProjectionTransformationMatrix(projmat);

			// the hierarchical rendering
			// Setup a black material that will be used by all the 
			// underlying objects			

			if(tss->m_ReplacingMaterial){
				tss->m_ReplacingMaterial->SetAsCurrent(rc);
				rc->SetTextureStageState(CKRST_TSS_STAGEBLEND,0,1);
				rc->SetOverriddenRendering(CK_OVERRIDE_USECURRENT,tss->m_ReplacingMaterial);
				rc->SetState(VXRENDERSTATE_LIGHTING,TRUE);			
			}else{
				tss->m_BlackMaterial->SetAsCurrent(rc);
				rc->SetTextureStageState(CKRST_TSS_STAGEBLEND,0,1);
				rc->SetOverriddenRendering(CK_OVERRIDE_USECURRENT,tss->m_BlackMaterial);
				rc->SetState(VXRENDERSTATE_LIGHTING,TRUE);
			}

			// And lock the material states to force 
			// so that material can not change the diffuse/emissive,settings
			rc->SetState(VXRENDERSTATE_LOCKMATERIALSTATES, true);
			for(int i = 0; i<CCustomPlayer::Instance().getShadowCasterNum(); i++){
				CK3dEntity *ent = CCustomPlayer::Instance().getShadowCasterObject(i);
				if(rc->IsObjectAttached(ent))
					HierarchicalRendering(ent,rc,tss);	
			}
			rc->SetState(VXRENDERSTATE_LOCKMATERIALSTATES, false);
			// Nicov : to solve mantis bug 0000830
			// In the case when an object is both a shadow caster and shadow receiver the following scenario may happen :			
			// Object is rendered in texture by Shadow caster BB -> its update timestamps is now n due to rendering (n is the last scene timestamp, this
			// BB is invoked *after* the render)
			// Object is added a channel as (a receiver) by BB with modification time stamp set to n.
			// Object shadow receiving channel is rendered at next frame, but not updated (time stamp is ok because of precedent rendering) 
			// -> The channel is not updated (vertex format is not ok) -> crash at next render time
			// Just incrementing the context time stamp will guarantee that its channel get updated
			ctx->m_TimeStamp++;



			rc->SetOverriddenRendering(CK_OVERRIDE_NONE);

			VxRect screen(0.0f,0.0f,(float)tex->GetWidth(),(float)tex->GetHeight());
			DrawFillRectangleGradient(rc,screen,shadowColor,shadowColor);

			if (softshadow) { // we draw a gradient rectangle from white to black, top to bottom
				VxColor white(1.0f,1.0f,1.0f,1.0f);
				VxColor black(0.2f,0.2f,0.2f,1.0f);
				DrawFillRectangleGradient(rc,screen,white,black);
			}

			///
			// we restoring matrices the transfo mat
			rc->SetWorldTransformationMatrix(oldWordMatrix);
			rc->SetProjectionTransformationMatrix(oldProjMat);
			rc->SetViewTransformationMatrix(oldViewMat);

			rc->SetState(VXRENDERSTATE_ZENABLE ,TRUE);

		}

		// Some rasterizer will skip this phase as they directly
		// render to the texture but others (OpenGl) may need
		// to update the content of the texture at this moment
		rc->BackToFront((CK_RENDER_FLAGS)RenderOptions);


	}

	if(srt)
		rc->SetRenderTarget(NULL);

	// we restore the old render context values
	rc->AttachViewpointToCamera(oldcam);
	// We need to reprepare the cameras...
	DWORD RenderOptions = rc->GetCurrentRenderOptions();
	rc->PrepareCameras((CK_RENDER_FLAGS)RenderOptions);
	BackgroundMat->SetTexture(oldtex);
	BackgroundMat->SetDiffuse(oldcol);

	minz = maxz;
	return render;
}

CKBOOL  ShadowCaster::do_ShadowCaster(/*const CKBehaviorContext& behcontext*/CKRenderContext *Dev,/*CKRenderObject* ro,*/void *Argument)
{
	//CKBehavior* beh = behcontext.Behavior;
	//CKContext* ctx	= behcontext.Context;	
 	CKContext* ctx = CCustomPlayer::Instance().GetCKContext();
 
	//for (CKObject** shadowIterator = shadowArray.Begin(); shadowIterator != shadowArray.End(); ++shadowIterator) {
		//CK3dEntity *ent = (CK3dEntity*)ro;
//	CK3dEntity *ent = CCustomPlayer::Instance().getShadowCasterGroup();
	int objNum = CCustomPlayer::Instance().getShadowCasterNum();
	if(objNum <= 0)
		return FALSE;
//	tss[0]->m_ShadowTexture->Create(shadowSize,shadowSize);	
	//CK3dEntity *ent = CCustomPlayer::Instance().getShadowCasterObject(0);
	//if(!ent) 
	//	return FALSE;
	//}
 	//CK3dEntity *ent = (CK3dEntity *) beh->GetTarget();
 	//if( !ent ) return CKBR_OWNERERROR;
 
 	//CKRenderContext *rcontext = behcontext.CurrentRenderContext;
 /*	CKRenderContext *rcontext = CCustomPlayer::Instance().GetRenderContext();*/
	CKRenderContext *rcontext = Dev;
 
 //	ShadowCasterStruct* tss = (ShadowCasterStruct*) beh->GetLocalParameterReadDataPtr(0);
	/*ShadowCasterStruct* tss = new ShadowCasterStruct;*/
	//loadShadowCaster(tss);
	//if (tss == 0){
	//	tss = new ShadowCasterStruct;
	//	loadShadowCaster(tss);
	//}
	//ShadowCasterStruct *tss = static_cast<ShadowCasterStruct*>(Argument);
	//ShadowCasterStruct* tss = (ShadowCasterStruct*) Argument;
	//int index = CCustomPlayer::Instance().findIndex(rcontext->GetID());
	int index = 0;

 	//CKMaterial *cmat = (CKMaterial*)tss[index]->m_Material;
 
 	//CKAttributeManager* attman = behcontext.AttributeManager;
 	CKAttributeManager* attman = CCustomPlayer::Instance().GetCKAttributeManger();
 	const XObjectPointerArray& oArray = attman->GetGlobalAttributeListPtr(tss[index]->m_ReceiverAttribute);
 
 	//if (beh->IsInputActive(1)) { // We enter by off
 	//	beh->ActivateInput(1, FALSE);
 	//	beh->ActivateOutput(1, TRUE);
 
 	//	// we remove the channels
 	//	for (CKObject** it = oArray.Begin(); it != oArray.End(); ++it) {
 	//		CK3dEntity *o = (CK3dEntity*)*it;
 	//		if(!o) continue;
 
 	//		CKMesh *mesh = (CKMesh*) o->GetCurrentMesh();
 	//		if(!mesh) continue;
 
 	//		mesh->RemoveChannel(cmat);
 	//	}
 
 
 	//	return CKBR_OK;
 	//}
 
 	//if(beh->IsInputActive(0)) { // We enter by on
 	//	beh->ActivateInput(0, FALSE);
 	//	beh->ActivateOutput(0, TRUE);
 	//}
 	// No receivers, we skip the whole thing
 	if (!oArray.Size()) return CKBR_ACTIVATENEXTFRAME;
 
 	float fov = 0.2f;
 	tss[index]->m_Camera->SetFov(fov);
 	float tanfov = tanf(fov*0.5f);
 
 	// READING INPUTS
 	// we get the input texture id
 //	CKLight* light = (CKLight*)beh->GetInputParameterObject(0);
	CKLight *light;
	if(lightID != -1){
		light = (CKLight *)CCustomPlayer::Instance().GetCKContext()->GetObject(lightID);
	}else{
		light = (CKLight*)CCustomPlayer::Instance().GetCKContext()->CreateObject(CKCID_LIGHT, "light"); /*= new CKLight;*/
		lightID = light->GetID();
	}
	if (!light) return CKBR_ACTIVATENEXTFRAME;
//	light->SetPosition(0.0f, 10.0f, 0.0f);
	float newY = sin(angle*3.1415926/180)*lightPos.y;
	float newX = lightPos.x+cos(angle*3.1415926/180)*lightPos.y;
	light->SetPosition(newX, newY, lightPos.z);
	light->SetType(VX_LIGHTPOINT);

 	CKBOOL perspective = TRUE;
 	if (light->GetType() == VX_LIGHTDIREC) perspective = FALSE;
 
// 	VxVector barycenter(0.0f, 0.0f, 0.0f);
//	VxVector bc;
//	float num = 0.0f;
//	for(int i = 0; i<objNum/*ent->GetChildrenCount()*/; i++){
//		/*ent->GetChild(i)->GetBaryCenter(&bc);*/
////		if(rcontext->IsObjectAttached(CCustomPlayer::Instance().getShadowCasterObject(i))){
//			CCustomPlayer::Instance().getShadowCasterObject(i)->GetBaryCenter(&bc);
//			barycenter += bc;
//			num++;
////		}
//	}
//	
//	if(num == 0){
//		//for (CKObject** it = oArray.Begin(); it != oArray.End(); ++it) {
//		//	CK3dEntity *o = (CK3dEntity*)*it;
//		//	if(!o) continue;
//		//	if(rcontext->IsObjectAttached(o)==FALSE)
//		//		continue;
//
//		//	CKMesh *mesh = (CKMesh*) o->GetCurrentMesh();
//		//	if(!mesh) continue;
//		//	int channel = mesh->GetChannelByMaterial( tss[index]->m_Material );
//		//	mesh->ActivateChannel(channel, FALSE);
//		//}
//		return CKBR_ACTIVATENEXTFRAME;
//	}
//	barycenter = barycenter/num;
	//barycenter /= ent->GetChildrenCount();
//	barycenter.Set(bc.x, bc.y, bc.z);
 //	ent->GetBaryCenter(&barycenter);
	ShadowCaster::Instance()->caculateBaryCenter();
 
 	// Light ranges
 //	float lmin = 5.0f;
	float lmin = minLightDistance;
 //	beh->GetInputParameterValue(1,&lmin);
 //	float lmax = 50.0f;
	float lmax = maxLightDistance;
 //	beh->GetInputParameterValue(2,&lmax);
 	CKBOOL softshadow = isSoft;
 //	beh->GetInputParameterValue(3,&softshadow);
 
 	// Rendering objects to shadow texture
 	float minz;
 	float retrievedMaxx, retrievedMaxy;

	VxVector posTest;
	tss[index]->m_Camera->GetPosition(&posTest);

 	CKBOOL inrange = RenderObjectsToTexture(ctx,/*ent,*/rcontext,tss[index],light,lmin,lmax,minz,softshadow, &retrievedMaxx, &retrievedMaxy);

	tss[index]->m_Camera->GetPosition(&posTest);
 
 	const VxMatrix& InvWorldMat = tss[index]->m_Camera->GetInverseWorldMatrix();
 
 	VxFrustum lightFrustum;
 	{
 		int width,height;
 		VxVector pos,dir,up,right;
 		tss[index]->m_Camera->GetPosition(&pos);
 		tss[index]->m_Camera->GetOrientation(&dir,&up,&right);
 		tss[index]->m_Camera->GetAspectRatio(width,height);
 		lightFrustum = VxFrustum(pos,right,up,dir,tss[index]->m_Camera->GetFrontPlane(),tss[index]->m_Camera->GetBackPlane(),tss[index]->m_Camera->GetFov(),(float)height/width);
 	}
 
 
 	VxVector pts[8];
 	for (CKObject** it = oArray.Begin(); it != oArray.End(); ++it) {
 		CK3dEntity *o = (CK3dEntity*)*it;
 		if(!o) continue;
		if(rcontext->IsObjectAttached(o)==FALSE)
			continue;
 
 		CKMesh *mesh = (CKMesh*) o->GetCurrentMesh();
 		if(!mesh) continue;
 
 		if (!inrange) {
 			int channel = mesh->GetChannelByMaterial( tss[index]->m_Material );
 			if (channel >= 0) mesh->ActivateChannel(channel,FALSE);
 			continue;
 		}
 
 		const VxBbox& box = o->GetBoundingBox(TRUE);
 		const VxMatrix& mat1 = o->GetWorldMatrix();
 
 		CKBOOL allOutside = !(VxIntersect::FrustumOBB(lightFrustum,o->GetBoundingBox(TRUE),o->GetWorldMatrix()));
 
 		int channel = mesh->GetChannelByMaterial( tss[index]->m_Material );
 		if (allOutside) {
 			mesh->ActivateChannel(channel,FALSE);
			for (int i = 0; i<4; i++)
			{
				if(i == index)
					continue;
				int  ch = mesh->GetChannelByMaterial(tss[i]->m_Material);
				if(ch>=0)
					mesh->ActivateChannel(ch, FALSE);
			}
 			continue; 
 		} else {
 			if( channel < 0 ){ // The object wasn't having yet the material as channel
 				channel = mesh->AddChannel( tss[index]->m_Material, FALSE);
 				// Channel Lighting
 				mesh->LitChannel(channel,FALSE);
 				// Channel blending
 				mesh->SetChannelSourceBlend(channel,VXBLEND_ZERO);
 				mesh->SetChannelDestBlend(channel,VXBLEND_SRCCOLOR);
 			}
 			mesh->ActivateChannel(channel);
			//for (int i = 0; i<4; i++)
			//{
			//	if(i == index)
			//		continue;
			//	int  ch = mesh->GetChannelByMaterial(tss[i]->m_Material);
			//	if(ch>=0)
			//		mesh->ActivateChannel(ch, FALSE);
			//}
 		}
 
 		if( o->IsVisible() &&  !o->IsAllOutsideFrustrum()){
 			CKDWORD Stride,cStride;
 			VxVector*	VertexArray;
 
 			VertexArray = (VxVector*) mesh->GetPositionsPtr( &Stride );
 			int verticescount	= mesh->GetVertexCount();
 
 			VxScratch tmp(verticescount*sizeof(float));
 			DWORD* VtxFlags		= (DWORD *)tmp.Mem();
 
 			VxVector camdir;
 			tss[index]->m_Camera->GetOrientation(&camdir,NULL,NULL,o);
 			camdir.Normalize();
 
 			// We have the choice for two techniques :
 			// - If Device Supports projected texture => instead of computing textures by ourselves
 			// we use the texture matrix to compute the coordinates 
 			// which will be divide by Z during rasterization
 			// - Otherwise we compute the texture coordinates
 			// at the vertex level and this requires that objects on which 
 			// the shadow is projected have a good tesselation
 			VxDriverDesc* drDesc = NULL;
 			/*CKRenderContext* rdCtx = behcontext.CurrentRenderContext;*/
 			/*CKRenderContext* rdCtx = CCustomPlayer::Instance().GetRenderContext();*/
			CKRenderContext* rdCtx = Dev;
 			if (rdCtx) {
 				int currentdriver;
 				currentdriver = rdCtx->GetDriverIndex();
 			//	drDesc = behcontext.Context->GetRenderManager()->GetRenderDriverDescription(currentdriver);
 				drDesc = CCustomPlayer::Instance().GetCKContext()->GetRenderManager()->GetRenderDriverDescription(currentdriver);
 			}
 
 			if (perspective && drDesc && (drDesc->Caps3D.CKRasterizerSpecificCaps & CKRST_SPECIFICCAPS_HARDWARETL) && (drDesc->Caps3D.TextureCaps & CKRST_TEXTURECAPS_PROJECTED)) {
 				// PROJECTED TEXTURES
 				// Ok We can do it with an effect that will generate the appropriate texture coordinates
 
 				tss[index]->m_Material->SetEffect(VXEFFECT_TEXGENREF);
 				CKParameter* param = tss[index]->m_Material->GetEffectParameter();
 				VX_EFFECTTEXGEN Tg = VXEFFECT_TGPROJECT;
 				CK_ID Id = CKOBJID(tss[index]->m_Camera);
 				if (param) {
 					CKStructHelper sh(param);
 					sh[0]->SetValue(&Tg);
 					sh[1]->SetValue(&Id);
 				}
 
 				// We still have to find the list of faces on which the 
 				// shadow should appear...
 				VxVector vPos;
 				Vx3DMultiplyMatrixVector(&vPos,InvWorldMat,&barycenter);
 				VxMatrix mat2;
 				Vx3DMultiplyMatrix(mat2,InvWorldMat,o->GetWorldMatrix());
 				float barycenterZ	= vPos.z;
 			/*	if (beh->GetVersion() > 0x00010000) {*/
 	//				barycenterZ = 0.001f;
 					/*beh->GetLocalParameterValue(3,&barycenterZ);
 				}*/
 
 				VxFrustum frustum(VxVector::axis0(),VxVector::axisX(),VxVector::axisY(),VxVector::axisZ(),barycenterZ,tss[index]->m_Camera->GetBackPlane(),fov,1.0f);
 
 				// calculate the uvs for the vertices
 				for (int i=0; i<verticescount; i++, VertexArray = (VxVector*)( (BYTE*)VertexArray + Stride)){
 
 					// Tranform Vertex Position from mesh coordinates system to projector coordinate system
 					Vx3DMultiplyMatrixVector(&vPos,mat2,VertexArray);
 
 					VtxFlags[i] = frustum.Classify(vPos);
 				} // for
 
 			} else{
 				// SOFTWARE computing of texture coordinates
 
 				tss[index]->m_Material->SetEffect(VXEFFECT_NONE);
 				Vx2DVector*	uvarray;
 
 				uvarray		= (Vx2DVector*) mesh->GetTextureCoordinatesPtr(&cStride, channel );
 				if( !uvarray ) continue;
 
 
 				VxVector vPos;
 				Vx3DMultiplyMatrixVector(&vPos,InvWorldMat,&barycenter);
 
 				VxMatrix mat2;
 				Vx3DMultiplyMatrix(mat2,InvWorldMat,o->GetWorldMatrix());
 
 				float barycenterZ	= vPos.z;
 			/*	if (beh->GetVersion() > 0x00010000) {*/
 //					barycenterZ = 0.001f;
 					/*beh->GetLocalParameterValue(3,&barycenterZ);
 				}*/
 
 				if (perspective) {
 					VxFrustum frustum(VxVector::axis0(),VxVector::axisX(),VxVector::axisY(),VxVector::axisZ(),barycenterZ,tss[index]->m_Camera->GetBackPlane(),fov,1.0f);
 
 					// calculate the uvs for the vertices
 					float invzfov;
 					for (int i=0; i<verticescount; i++, VertexArray = (VxVector*)( (BYTE*)VertexArray + Stride)
 						,uvarray = (Vx2DVector*)( (BYTE*)uvarray + cStride ) ){
 
 							// Tranform Vertex Position from mesh coordinates system to projector coordinate system
 							Vx3DMultiplyMatrixVector(&vPos,mat2,VertexArray);
 
 							VtxFlags[i] = frustum.Classify(vPos);
 
 							invzfov		= 0.5f/(tanfov*vPos.z);
 							uvarray->x	= 0.5f + vPos.x*invzfov;
 							uvarray->y	= 0.5f - vPos.y*invzfov;
 					} // for
 				} else {

 
 					//VxVector scale;
 					//ent->GetScale(&scale, FALSE);
 					const Vx2DVector tiling = Vx2DVector( 0.5f/retrievedMaxx, 1.0f/retrievedMaxy );
 
 					// Add planar mapping information to the matrix
 					mat2[0][0] *= tiling.x; mat2[0][1] *= -tiling.y;
 					mat2[1][0] *= tiling.x; mat2[1][1] *= -tiling.y;
 					mat2[2][0] *= tiling.x; mat2[2][1] *= -tiling.y;
 
 					mat2[3][0] *= tiling.x; mat2[3][1] *= -tiling.y; 
 					mat2[3][0] += 0.5f; mat2[3][1] += 0.5f; 
 
 					// TODO : this is shitty
 					// utiliser une matrice (comme dans le planar mapping
 					// et ne pas faire de look at sur la camera, mais la deplacer...
 					for (int i=0; i<verticescount; i++, VertexArray = (VxVector*)( (BYTE*)VertexArray + Stride)
 						,uvarray = (Vx2DVector*)( (BYTE*)uvarray + cStride ) ){
 
 							// Tranform Vertex Position from mesh coordinates system to projector coordinate system
 							Vx3DMultiplyMatrixVector(&vPos,mat2,VertexArray);
 
 							uvarray->x	= vPos.x;
 							uvarray->y	= vPos.y;
 
 							VtxFlags[i]=0;
 
 							if (vPos.z < barycenterZ)		VtxFlags[i] = VXCLIP_FRONT;
 							if (uvarray->x < 0)		VtxFlags[i] |= VXCLIP_LEFT;
 							if (uvarray->x > 1.0f)	VtxFlags[i] |= VXCLIP_RIGHT;
 							if (uvarray->y < 0)		VtxFlags[i] |= VXCLIP_TOP;
 							if (uvarray->y > 1.0f)	VtxFlags[i] |= VXCLIP_BOTTOM;
 
 					} // for
 				}
 			}	
 
 			int FaceCount		= mesh->GetFaceCount();
 			CKVINDEX* Indices		= mesh->GetFacesIndices();
 			CKDWORD NormStride	= 0;
 			BYTE* FaceNormal	= mesh->GetFaceNormalsPtr(&NormStride);
 			XArray<CKVINDEX>	channelIndices;	
 
 			for (int i=0; i<FaceCount; ++i, Indices+=3, FaceNormal+=NormStride) {
 				// Face is corretly oriented to be mapped , check if 
 				// all its vertices are inside the projection frustrum ( z >0 for the moment)
 				if (!((VtxFlags[Indices[0]]&VtxFlags[Indices[1]]&VtxFlags[Indices[2]]))
 					&& (DotProduct(*(VxVector *)FaceNormal,camdir) < 0)) {
 						channelIndices.PushBack(Indices[0]);
 						channelIndices.PushBack(Indices[1]);
 						channelIndices.PushBack(Indices[2]);
 				}
 			}
 			if (!channelIndices.Size())
 				mesh->ActivateChannel(channel,FALSE);
 			else {
 				mesh->UVChanged(channel);
 				mesh->SetChannelFaceIndices(channel,channelIndices.Size(),channelIndices.Begin());
 			}
 		} else {
 			mesh->ActivateChannel(channel,FALSE);
 		}
 	} // for

	return CKBR_OK;
}

CKERROR ShadowCaster::loadShadowCaster(ShadowCasterStruct* tss, int index)
{
/*	char* materialname = "SC Material";*/

	CKContext* ctx	= CCustomPlayer::Instance().GetCKContext();

	// creation of a CkTexture
	CKCamera* cam = (CKCamera*)ctx->CreateObject(CKCID_TARGETCAMERA,cameraname[index],/*beh->IsDynamic()?*/CK_OBJECTCREATION_DYNAMIC/*:CK_OBJECTCREATION_NONAMECHECK*/);
	cam->ModifyObjectFlags(CK_OBJECT_NOTTOBELISTEDANDSAVED,0);

	// creation of a CkTexture
	CKTexture* tex=(CKTexture*)ctx->CreateObject(CKCID_TEXTURE,texturename[index],/*beh->IsDynamic()?*/CK_OBJECTCREATION_DYNAMIC/*:CK_OBJECTCREATION_NONAMECHECK*/);
	tex->ModifyObjectFlags(CK_OBJECT_NOTTOBELISTEDANDSAVED,0);

	// creation of a CkMaterial
	CKMaterial* mat = (CKMaterial*)ctx->CreateObject(CKCID_MATERIAL,materialname[index],/*beh->IsDynamic()?*/CK_OBJECTCREATION_DYNAMIC/*:CK_OBJECTCREATION_NONAMECHECK*/);
	mat->ModifyObjectFlags(CK_OBJECT_NOTTOBELISTEDANDSAVED,0);
	mat->SetDiffuse(VxColor(1.0f,1.0f,1.0f,1.0f));
	mat->SetSpecular(VxColor(1.0f,1.0f,1.0f));
	mat->SetPower(0.0f);
	mat->SetTexture(tex);
	mat->SetTextureBlendMode(VXTEXTUREBLEND_DECAL);
	mat->SetTextureAddressMode(VXTEXTURE_ADDRESSBORDER);
	mat->SetTextureBorderColor(0xffffffff);
	mat->SetTextureMinMode(VXTEXTUREFILTER_LINEAR);
	mat->SetTextureMagMode(VXTEXTUREFILTER_LINEAR);
	mat->SetTextureBorderColor(0xFFFFFFFF);

	// creation of a black CkMaterial
	CKMaterial* bmat = (CKMaterial*)ctx->CreateObject(CKCID_MATERIAL,blackmaterialname[index],/*beh->IsDynamic()?*/CK_OBJECTCREATION_DYNAMIC/*:CK_OBJECTCREATION_NONAMECHECK*/);
	VxColor blackColor(0.0f,0.0f,0.0f,1.0f);;
	bmat->ModifyObjectFlags(CK_OBJECT_NOTTOBELISTEDANDSAVED,0);
	bmat->SetDiffuse(blackColor);
	bmat->SetEmissive(blackColor);
	bmat->SetSpecular(blackColor);
	bmat->SetPower(0.0f);

	tss->m_Camera			= cam;
	tss->m_ShadowTexture	= tex;
	tss->m_Material			= mat;
	tss->m_BlackMaterial	= bmat;

	tss->m_ShadowTexture->Create(shadowSize, shadowSize);
 
// 	int size = 64;

// 	size = (Near2Power(size));
// 	if (size != tss->m_ShadowTexture->GetWidth()) { // Size changes
// 		tss->m_ShadowTexture->Create(size,size);			  
// 	}

	//if(beh->GetLocalParameterCount() > 2){
	//	tss->m_ReplacingMaterial = (CKMaterial*) beh->GetLocalParameterObject(2);				
	//}else{
		tss->m_ReplacingMaterial = NULL;
	//}

	CKAttributeManager* attman = ctx->GetAttributeManager();
	/*int att = attman->RegisterNewAttributeType("Shadow Caster Receiver",CKPGUID_NONE,CKCID_3DOBJECT);*/
	int  att = attman->RegisterNewAttributeType("Shadow Caster Receiver",CKPGUID_NONE,CKCID_3DENTITY);
	tss->m_ReceiverAttribute= att;
	tss->m_Camera->SetAspectRatio(1,1);

	return CKBR_OK;
}

void ShadowCaster::setShadowSize(int size)
{
//	ShadowCasterStruct *tss = (ShadowCasterStruct*) beh->GetLocalParameterWriteDataPtr(0);

	/*int size = 64;
	beh->GetLocalParameterValue(1,&size);*/
	
	shadowSize = (Near2Power(size));
//	if (shadowSize != tss[0]->m_ShadowTexture->GetWidth()) { // Size changes
		tss[0]->m_ShadowTexture->Create(shadowSize,shadowSize);			
//	}
}

void ShadowCaster::ShadowCasterAttributeCallback(int AttribType,BOOL Set,CKBeObject *obj,void *arg)
{
	if(!Set) {
		CK3dObject* o = (CK3dObject*)obj;
		if(!o) return;

		CKMesh* mesh = o->GetCurrentMesh();
		if(!mesh) return;

		for (int i=0; i<mesh->GetChannelCount();) {
			CKMaterial* mat = mesh->GetChannelMaterial(i);
			if (mat &&  !strcmp(mat->GetName(),materialname[0])) {
				mesh->RemoveChannel(i);
			}else if (mat &&  !strcmp(mat->GetName(),materialname[1])) {
				mesh->RemoveChannel(i);
			}else if (mat &&  !strcmp(mat->GetName(),materialname[2])) {
				mesh->RemoveChannel(i);
			}else if (mat &&  !strcmp(mat->GetName(),materialname[3])) {
				mesh->RemoveChannel(i);
			}else ++i;
		}
// #ifndef NO_SHADER	
// #if 0
// 		CKShaderManager* shaderManager = (CKShaderManager*) (mesh->GetCKContext()->GetManagerByGuid(ShaderManagerGUID));
// 		CKShader* shShader = shaderManager ? shaderManager->GetShadowShader() : NULL;
// 		if (shShader)
// 		{			
// 			for (int i=0; i<mesh->GetChannelCount();) 
// 			{
// 				CKMaterialShader* matSh = mesh->GetChannelShaderOverride(i);
// 				if (matSh && matSh->m_Shader == shShader)
// 				{
// 					mesh->RemoveChannel(i);
// 				}
// 				else ++i;
// 			}
// 		}
// #endif
// #endif
	} 
}

CKERROR ShadowCaster::deleteShadowCaster(){
	if (!tss[0]) return CK_OK;
	CKMaterial* mat = tss[0]->m_Material;

	// now we remove the ShadowCaster channel
	CKAttributeManager* attman = CCustomPlayer::Instance().GetCKContext()->GetAttributeManager();
	const XObjectPointerArray oArray = attman->GetAttributeListPtr(tss[0]->m_ReceiverAttribute);

	for (CKObject** it = oArray.Begin(); it != oArray.End(); ++it) {
		CK3dEntity* o = (CK3dEntity*)*it;
		if(!o) continue;

		CKMesh* mesh = o->GetCurrentMesh();
		if(!mesh) continue;

		mesh->RemoveChannel( mat );
	}

	CKDestroyObject(tss[0]->m_ShadowTexture);
	CKDestroyObject(tss[0]->m_Material);
	CKDestroyObject(tss[0]->m_Camera);
//	tss[index] = 0;
	return CKBR_OK;
}

void ShadowCaster::caculateBaryCenter(){
	int objNum = CCustomPlayer::Instance().getShadowCasterNum();
	if(objNum <= 0)
		return;
	barycenter.Set(0.0f, 0.0f, 0.0f);
	VxVector bc;
	float num = 0.0f;
	for(int i = 0; i<objNum; i++){
		CCustomPlayer::Instance().getShadowCasterObject(i)->GetBaryCenter(&bc);
		barycenter += bc;
		num++;
	}
	barycenter = barycenter/num;
}

void ShadowCaster::caculateEntBox(){
	if(CCustomPlayer::Instance().getShadowCasterNum()==0)
		return;
	minV = CCustomPlayer::Instance().getShadowCasterObject(0)->GetHierarchicalBox().Min;
	maxV = CCustomPlayer::Instance().getShadowCasterObject(0)->GetHierarchicalBox().Max;

	for(int i = 0; i<CCustomPlayer::Instance().getShadowCasterNum(); i++){
		//	if(rc->IsObjectAttached(CCustomPlayer::Instance().getShadowCasterObject(i))){
		VxVector tempMin = CCustomPlayer::Instance().getShadowCasterObject(i)->GetHierarchicalBox().Min;
		VxVector tempMax = CCustomPlayer::Instance().getShadowCasterObject(i)->GetHierarchicalBox().Max;
		if(tempMin.x<minV.x)
			minV.x = tempMin.x;
		if(tempMin.y<minV.y)
			minV.y = tempMin.y;
		if(tempMin.z<minV.z)
			minV.z = tempMin.z;
		if(tempMax.x>maxV.x)
			maxV.x = tempMax.x;
		if(tempMax.y>maxV.y)
			maxV.y = tempMax.y;
		if(tempMax.z>maxV.z)
			maxV.z = tempMax.z;
		//	}
	}
	//float tempX = (lightPos.x- minV.x)>(lightPos.x-maxV.x)?(lightPos.x-maxV.x):(lightPos.x-maxV.x);
	//float tempZ = (lightPos.z - minV.z)>(lightPos.z-maxV.z)?(lightPos.y-minV.z):(lightPos.y-maxV.z);
	//minV.Set(lightPos.x-tempX, minV.y, lightPos.z-tempZ);
	//maxV.Set(lightPos.x+tempX, maxV.y, lightPos.z+tempZ);
}

void ShadowCaster::setLightPos(){
	lightPos.x = barycenter.x;
	lightPos.z = barycenter.z;
	lightPos.y = lightRadius;
}

void ShadowCaster::setAngle(int a){
	angle = a/8.00f;
}

bool ShadowCaster::eventCallback(CKRenderContext *context,void *Argument){
	CKInputManager* mInput = CCustomPlayer::Instance().GetInputManager();
	if(mInput->IsMouseButtonDown(CK_MOUSEBUTTON_LEFT) == TRUE){
		Vx2DVector mousePos;
		mInput->GetMousePosition(mousePos,TRUE);
		CCustomPlayer::Instance().removePostRenderCallBack(CK_RENDERCALLBACK(ShadowCaster::eventCallback), NULL);
	}
	return true;
}